/** \file   DenseTag.hpp
 *  \author Jason Kraftcheck
 *  \date   2010-12-14
 */

#ifndef DENSE_TAG_HPP
#define DENSE_TAG_HPP

#include "TagInfo.hpp"

namespace moab
{

class EntitySequence;

/**\brief Dense storage of fixed-length tag data
 *
 * Implement normal dense tag.
 */
class DenseTag : public TagInfo
{
  private:
    int mySequenceArray;  //!< Array index in SequenceManager used to store tag data.

    unsigned char* meshValue;

    DenseTag( int array_index, const char* name, int size, DataType type, const void* default_value );

  public:
    static DenseTag* create_tag( SequenceManager* seqman,
                                 Error* error,
                                 const char* name,
                                 int bytes,
                                 DataType type,
                                 const void* default_value );

    virtual ~DenseTag();

    virtual TagType get_storage_type() const;

    /**\brief Remove/clear tag data for all entities
     *
     * Remove tag values from entities.
     *
     *\param delete_pending  If true, then release any global
     *          data associated with the tag in preparation for deleting
     *          the tag itself.
     *
     *\Note Invalidates tag if \c tag_delete_pending is true.  The only
     *        valid method that can be invoked that is is the destructor.
     *
     *\param seqman    Pointer to mesh entity database
     */
    virtual ErrorCode release_all_data( SequenceManager* seqman, Error* error_handler, bool delete_pending );

    /**\brief Get tag value for passed entities
     *
     * Get tag values for specified entities.
     *
     *\Note Will fail for variable-length data.
     *\param seqman Pointer to mesh entity database
     *\param entities Entity handles for which to retrieve tag data
     *\param num_entities Length of \c entities array
     *\param data Pointer to memory in which to store consecutive tag values,
     *            one for each passed entity.
     */
    virtual ErrorCode get_data( const SequenceManager* seqman,
                                Error* error_handler,
                                const EntityHandle* entities,
                                size_t num_entities,
                                void* data ) const;

    /**\brief Get tag value for passed entities
     *
     * Get tag values for specified entities.
     *
     *\Note Will fail for variable-length data.
     *\param seqman Pointer to mesh entity database
     *\param entities Entity handles for which to retrieve tag data
     *\param data Pointer to memory in which to store consecutive tag values,
     *            one for each passed entity.
     */
    virtual ErrorCode get_data( const SequenceManager* seqman,
                                Error* error_handler,
                                const Range& entities,
                                void* data ) const;

    /**\brief Get tag value for passed entities
     *
     * Get tag values for specified entities.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to retrieve tag data
     *\param num_entities Length of \c entities array
     *\param data_ptrs Array of pointers to tag values, one pointer
     *                 for each passed entity.
     *\param data_lengths One value for each entity specifying the
     *                length of the tag value for the corresponding
     *                entity.
     */
    virtual ErrorCode get_data( const SequenceManager* seqman,
                                Error* error_handler,
                                const EntityHandle* entities,
                                size_t num_entities,
                                const void** data_ptrs,
                                int* data_lengths ) const;

    /**\brief Get tag value for passed entities
     *
     * Get tag values for specified entities.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to retrieve tag data
     *\param data_ptrs Array of pointers to tag values, one pointer
     *                 for each passed entity.
     *\param data_lengths One value for each entity specifying the
     *                length of the tag value for the corresponding
     *                entity.
     */
    virtual ErrorCode get_data( const SequenceManager* seqman,
                                Error* error_handler,
                                const Range& entities,
                                const void** data_ptrs,
                                int* data_lengths ) const;

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values
     *\Note Will fail for variable-length data.
     *\param seqman Pointer to mesh entity database
     *\param entities Entity handles for which to store tag data
     *\param num_entities Length of \c entities array
     *\param data Pointer to memory holding consecutive tag values,
     *            one for each passed entity.
     */
    virtual ErrorCode set_data( SequenceManager* seqman,
                                Error* error_handler,
                                const EntityHandle* entities,
                                size_t num_entities,
                                const void* data );

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values
     *\Note Will fail for variable-length data.
     *\param seqman Pointer to mesh entity database
     *\param entities Entity handles for which to store tag data
     *\param data Pointer to memory holding consecutive tag values,
     *            one for each passed entity.
     */
    virtual ErrorCode set_data( SequenceManager* seqman,
                                Error* error_handler,
                                const Range& entities,
                                const void* data );

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     *\param num_entities Length of \c entities array
     *\param data_ptrs Array of pointers to tag values, one pointer
     *                 for each passed entity.
     *\param data_lengths One value for each entity specifying the
     *                length of the tag value for the corresponding
     *                entity.  Array is required for variable-length
     *                tags and is ignored for fixed-length tags.
     */
    virtual ErrorCode set_data( SequenceManager* seqman,
                                Error* error_handler,
                                const EntityHandle* entities,
                                size_t num_entities,
                                void const* const* data_ptrs,
                                const int* data_lengths );

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     *\param data_ptrs Array of pointers to tag values, one pointer
     *                 for each passed entity.
     *\param data_lengths One value for each entity specifying the
     *                length of the tag value for the corresponding
     *                entity.  Array is required for variable-length
     *                tags and is ignored for fixed-length tags.
     */
    virtual ErrorCode set_data( SequenceManager* seqman,
                                Error* error_handler,
                                const Range& entities,
                                void const* const* data_ptrs,
                                const int* data_lengths );

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     *\param num_entities Length of \c entities array
     *\param value_ptr Pointer to a single tag value which is to be
     *                 stored for each of the passed entities.
     *\param value_len Length of tag value in bytes.  Ignored for
     *                 fixed-length tags.  Required for variable-
     *                 length tags.
     */
    virtual ErrorCode clear_data( SequenceManager* seqman,
                                  Error* error_handler,
                                  const EntityHandle* entities,
                                  size_t num_entities,
                                  const void* value_ptr,
                                  int value_len = 0 );

    /**\brief Set tag value for passed entities
     *
     * Store tag data or update stored tag values.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     *\param value_ptr Pointer to a single tag value which is to be
     *                 stored for each of the passed entities.
     *\param value_len Length of tag value in bytes.  Ignored for
     *                 fixed-length tags.  Required for variable-
     *                 length tags.
     */
    virtual ErrorCode clear_data( SequenceManager* seqman,
                                  Error* error_handler,
                                  const Range& entities,
                                  const void* value_ptr,
                                  int value_len = 0 );

    /**\brief Remove/clear tag data for entities
     *
     * Remove tag values from entities.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     *\param num_entities Length of \c entities array
     */
    virtual ErrorCode remove_data( SequenceManager* seqman,
                                   Error* error_handler,
                                   const EntityHandle* entities,
                                   size_t num_entities );

    /**\brief Remove/clear tag data for entities
     *
     * Remove tag values from entities.
     *
     *\param seqman    Pointer to mesh entity database
     *\param entities  Entity handles for which to store tag data
     */
    virtual ErrorCode remove_data( SequenceManager* seqman, Error* error_handler, const Range& entities );

    /**\brief Access tag data via direct pointer into contiguous blocks
     *
     * Iteratively obtain direct access to contiguous blocks of tag
     * storage.  This function cannot be used with bit tags because
     * of the compressed bit storage.  This function cannot be used
     * with variable length tags because it does not provide a mechanism
     * to determine the length of the value for each entity.  This
     * function may be used with sparse tags, but if it is used, it
     * will return data for a single entity at a time.
     *
     *\param iter        As input, the first entity for which to return
     *                   data.  As output, one past the last entity for
     *                   which data was returned.
     *\param end         One past the last entity for which data is desired
     *\param data_ptr    Output: pointer to tag storage.
     *
     *\Note If this function is called for entities for which no tag value
     *      has been set, but for which a default value exists, it will
     *      force the allocation of explicit storage for each such entity
     *      even though MOAB would normally not explicitly store tag values
     *      for such entities.
     */
    virtual ErrorCode tag_iterate( SequenceManager* seqman,
                                   Error* error_handler,
                                   Range::iterator& iter,
                                   const Range::iterator& end,
                                   void*& data_ptr,
                                   bool allocate = true );

    /**\brief Get all tagged entities
     *
     * Get the list of entities for which the a tag value has been set,
     * or a close approximation if the tag storage scheme cannot
     * accurately determine exactly which entities have explicit values.
     *
     *\param seqman   Pointer to entity storage database
     *\param output_entities Results *appended* to this range
     *\param type     Optional entity type.  If specified, search is
     *                limited to entities of specified type.
     *\param intersect Optional intersect list.  If specified,
     *                search is restricted to entities in this list.
     */
    virtual ErrorCode get_tagged_entities( const SequenceManager* seqman,
                                           Range& output_entities,
                                           EntityType type        = MBMAXTYPE,
                                           const Range* intersect = 0 ) const;

    /**\brief Count all tagged entities
     *
     * Count the entities for which the a tag value has been set,
     * or a close approximation if the tag storage scheme cannot
     * accurately determine exactly which entities have explicit values.
     *
     *\param seqman   Pointer to entity storage database
     *\param output_count This is *incremented* for each detected entity.
     *\param type     Optional entity type.  If specified, search is
     *                limited to entities of specified type.
     *\param intersect Optional intersect list.  If specified,
     *                search is restricted to entities in this list.
     */
    virtual ErrorCode num_tagged_entities( const SequenceManager* seqman,
                                           size_t& output_count,
                                           EntityType type        = MBMAXTYPE,
                                           const Range* intersect = 0 ) const;

    /**\brief Get all tagged entities with tag value
     *
     * Get the list of entities which have the specified tag value.
     *
     *\param seqman   Pointer to entity storage database
     *\param output_entities Results *appended* to this range
     *\param value    Pointer to tag value
     *\param value_bytes Size of tag value in bytes.
     *\param type     Optional entity type.  If specified, search is
     *                limited to entities of specified type.
     *\param intersect_entities Optional intersect list.  If specified,
     *                search is restricted to entities in this list.
     */
    virtual ErrorCode find_entities_with_value( const SequenceManager* seqman,
                                                Error* error_handler,
                                                Range& output_entities,
                                                const void* value,
                                                int value_bytes                 = 0,
                                                EntityType type                 = MBMAXTYPE,
                                                const Range* intersect_entities = 0 ) const;

    /**\brief Check if entity is tagged */
    virtual bool is_tagged( const SequenceManager*, EntityHandle h ) const;

    /**\brief Get memory use for tag data.
     *
     */
    virtual ErrorCode get_memory_use( const SequenceManager* seqman,
                                      unsigned long& total,
                                      unsigned long& per_entity ) const;

    /**\brief Get read-only tag storage
     *
     *\param seqman  Sequence manager
     *\param error   Error object through which to report errors
     *\param h       First entity for which to return storage.
     *\param ptr     Pointer to dag storage.  This pointer will be set
     *               to null and \c MB_SUCCESS will be returned if the handle
     *               is valid but no tag storage has been allocated.
     *\param count   Number of consecutive entities for which tag storage
     *               is returned.  This value will be valid even if null
     *               is returned for \c ptr and indicates the number of
     *               consecutive entities for which no tag storage has been
     *               allocated.
     *\return        MB_SUCCESS if handle is valid (regardless of whether
     *               or not any tag storage is allocated).
     */
    ErrorCode get_array( const SequenceManager* seqman,
                         Error* error,
                         EntityHandle h,
                         const unsigned char* const& ptr,
                         size_t& count ) const;

    /**\brief Get read-only tag storage for entire sequence
     *
     *\param seq     Sequence
     *\param ptr     Pointer to dag storage.  This pointer will be set
     *               to null and \c MB_SUCCESS will be returned if no tag storage has been
     *allocated. \return        MB_SUCCESS if sequence is valid (regardless of whether or not any
     *tag storage is allocated).
     */
    ErrorCode get_array( const EntitySequence* seq, const unsigned char* const& ptr ) const;

  private:
    DenseTag( const DenseTag& );
    DenseTag& operator=( const DenseTag& );

    /**\brief Get or allocated tag storage
     *
     *\param h       First entity for which to return storage.
     *\param ptr     Pointer to dag storage.  This pointer will be set
     *               to null and \c MB_SUCCESS will be returned if the handle
     *               is valid but no tag storage has been allocated.
     *\param count   Number of consecutive entities for which tag storage
     *               is returned.  This value will be valid even if null
     *               is returned for \c ptr and indicates the number of
     *               consecutive entities for which no tag storage has been
     *               allocated.
     *\param allocate If true storage will be allocated and initialized
     *               if it has not already been allocated.
     *\return        MB_SUCCESS if handle is valid (regardless of whether
     *               or not any tag storage is allocated).
     */
    inline ErrorCode get_array_private( SequenceManager* seqman,
                                        Error* error,
                                        EntityHandle h,
                                        unsigned char*& ptr,
                                        size_t& count,
                                        bool allocate );

    /**\brief Get tag storage
     *
     *\param h       First entity for which to return storage.
     *\param ptr     Pointer to dag storage.  This pointer will be set
     *               to null and \c MB_SUCCESS will be returned if the handle
     *               is valid but no tag storage has been allocated.
     *\param count   Number of consecutive entities for which tag storage
     *               is returned.  This value will be valid even if null
     *               is returned for \c ptr and indicates the number of
     *               consecutive entities for which no tag storage has been
     *               allocated.
     *\return        MB_SUCCESS if handle is valid (regardless of whether
     *               or not any tag storage is allocated).
     */
    inline ErrorCode get_array_private( const SequenceManager* seqman,
                                        Error* error,
                                        EntityHandle h,
                                        const unsigned char*& ptr,
                                        size_t& count ) const;

    /**\brief Get non-read-only tag storage for entire sequence
     *
     *\param seq     Sequence
     *\param ptr     Pointer to dag storage.  This pointer will be set
     *               to null and \c MB_SUCCESS will be returned if no tag storage has been
     *allocated. \return        MB_SUCCESS if sequence is valid (regardless of whether or not any
     *tag storage is allocated).
     */
    ErrorCode get_array_private( const EntitySequence* seq, const unsigned char*& ptr ) const;

    /**\brief Common implementation of public clear_data and remove_data */
    ErrorCode clear_data( bool allocate,
                          SequenceManager* seqman,
                          Error* error,
                          const EntityHandle* entities,
                          size_t num_entities,
                          const void* value_ptr );

    /**\brief Common implementation of public clear_data and remove_data */
    ErrorCode clear_data( bool allocate,
                          SequenceManager* seqman,
                          Error* error,
                          const Range& entities,
                          const void* value_ptr );
};

}  // namespace moab

#endif  // DENSE_TAG_HPP
